#pragma once

#include <iostream>
#include <stdint.h>

namespace Navinfo
{
	namespace Engine
	{
		namespace Math
		{

			template< typename T, unsigned int N >
			struct VectorBase
			{
			public:
				typedef T ValueType;

				VectorBase< T, N >() {}

				VectorBase< T, N >(const VectorBase< T, N >& rhs)
				{
					for (unsigned int i = 0; i < N; ++i)
						(*this)[i] = rhs[i];
				}

				const T& operator[](unsigned int i) const
				{
					return m_data[i];
				}

				T& operator[](unsigned int i)
				{
					return m_data[i];
				}

				bool operator ==(const VectorBase< T, N >& rhs) const
				{
					bool equal = true;
					for (unsigned int i = 0; i < N; ++i)
					if ((*this)[i] != rhs[i])
					{
						equal = false;
						break;
					}
					return equal;
				}

				virtual bool operator !=(const VectorBase< T, N >& rhs) const
				{
					return !((*this) == rhs);
				}

				VectorBase< T, N >& operator +=(const VectorBase< T, N >& rhs)
				{
					for (unsigned int i = 0; i < N; ++i)
						(*this)[i] += rhs[i];
					return *this;
				}
				VectorBase< T, N >& operator -=(const VectorBase< T, N >& rhs)
				{
					for (unsigned int i = 0; i < N; ++i)
						(*this)[i] -= rhs[i];
					return *this;
				}

				VectorBase< T, N >& operator *=(const VectorBase< T, N >& rhs)
				{
					for (unsigned int i = 0; i < N; ++i)
						(*this)[i] *= rhs[i];
					return *this;
				}

				VectorBase< T, N >& operator /=(const VectorBase< T, N >& rhs)
				{
					for (unsigned int i = 0; i < N; ++i)
						(*this)[i] /= rhs[i];
					return *this;
				}

				VectorBase< T, N >& operator-()
				{
					for (unsigned int i = 0; i < N; ++i)
						(*this)[i] = -(*this)[i];
					return *this;
				}

				VectorBase< T, N >& operator=(const VectorBase< T, N >& rhs)
				{
					for (unsigned int i = 0; i < N; ++i)
						(*this)[i] = rhs[i];
					return *this;
				}

				bool operator<(const VectorBase< T, N >& rhs) const
				{
					bool result = true;
					for (unsigned int i = 0; i < N; ++i)
					if ((*this)[i] >= rhs[i])
					{
						result = false;
						break;
					}
					return result;
				}

				bool operator>(const VectorBase< T, N >& rhs) const
				{
					bool result = true;
					for (unsigned int i = 0; i < N; ++i)
					if ((*this)[i] <= rhs[i])
					{
						result = false;
						break;
					}
					return result;
				}

			private:
				T m_data[N];
			};

			template< typename T, unsigned int N >
			VectorBase< T, N > operator+(const VectorBase< T, N >&lhs, const VectorBase< T, N >&rhs)
			{
				return VectorBase< T, N >(lhs) += rhs;
			}

			template< typename T, unsigned int N >
			VectorBase< T, N > operator-(const VectorBase< T, N >&lhs, const VectorBase< T, N >&rhs)
			{
				return VectorBase< T, N >(lhs) -= rhs;
			}

			template< typename T, unsigned int N >
			VectorBase< T, N > operator*(const VectorBase< T, N >&lhs, const VectorBase< T, N >&rhs)
			{
				return VectorBase< T, N >(lhs) *= rhs;
			}


			template< typename T, unsigned int N >
			VectorBase< T, N > operator/(const VectorBase< T, N >&lhs, const VectorBase< T, N >&rhs)
			{
				return VectorBase< T, N >(lhs) /= rhs;
			}

			//partial specialization for 2 component vectors
			template< typename T >
			struct VectorBase< T, 2 >
			{
				typedef T ValueType;
				union
				{
					struct
					{
						T x_;
						T y_;
					};
					T data[2];
				};

				VectorBase< T, 2 >() {}
				VectorBase< T, 2 >(T n) : x_(n), y_(n) {}
				VectorBase< T, 2 >(T _x, T _y) : x_(_x), y_(_y) {}
				template< typename U >
				VectorBase< T, 2 >(const VectorBase< U, 2 >& rhs) : x_(rhs.x_), y_(rhs.y_) {}

				const T& operator[](unsigned int i) const
				{
					return data[i];
				}

				T& operator[](unsigned int i)
				{
					return data[i];
				}

				bool operator ==(const VectorBase< T, 2 >& rhs) const
				{
					return ((x_ == rhs.x_) && (y_ == rhs.y_));
				}

				bool operator !=(const VectorBase< T, 2 >& rhs) const
				{
					return !((*this) == rhs);
				}

				VectorBase< T, 2 >& operator +=(const VectorBase< T, 2 >& rhs)
				{
					x_ += rhs.x_;
					y_ += rhs.y_;
					return *this;
				}

				VectorBase< T, 2 >& operator -=(const VectorBase< T, 2 >& rhs)
				{
					x_ -= rhs.x_;
					y_ -= rhs.y_;
					return *this;
				}

				VectorBase< T, 2 >& operator *=(const VectorBase< T, 2 >& rhs)
				{
					x_ *= rhs.x_;
					y_ *= rhs.y_;
					return *this;
				}

				VectorBase< T, 2 >& operator /=(const VectorBase< T, 2 >& rhs)
				{
					x_ /= rhs.x_;
					y_ /= rhs.y_;
					return *this;
				}

				VectorBase< T, 2 >& operator-()
				{
					x_ = -x_;
					y_ = -y_;
					return *this;
				}

				bool operator <(const VectorBase< T, 2 >& rhs) const
				{
					return (x_ < rhs.x_) && (y_ < rhs.y_);
				}

				bool operator >(const VectorBase< T, 2 >& rhs) const
				{
					return (x_ > rhs.x_) && (y_ > rhs.y_);
				}

				bool operator <=(const VectorBase< T, 2 >& rhs) const
				{
					return (x_ <= rhs.x_) && (y_ <= rhs.y_);
				}

				bool operator >=(const VectorBase< T, 2 >& rhs) const
				{
					return (x_ >= rhs.x_) && (y_ >= rhs.y_);
				}

			};

			//partial specialization for 3 component vectors
			template< typename T >
			struct VectorBase< T, 3 >
			{
				typedef T ValueType;
				union
				{
					struct
					{
						T x_;
						T y_;
						T z_;
					};
					T data[3];
				};

				VectorBase< T, 3 >() {}
				VectorBase< T, 3 >(T n) : x_(n), y_(n), z_(n) {}
				VectorBase< T, 3 >(T _x, T _y) : x_(_x), y_(_y), z_(0.0) {}
				VectorBase< T, 3 >(T _x, T _y, T _z) : x_(_x), y_(_y), z_(_z) {}
				template< typename U >
				VectorBase< T, 3 >(const VectorBase< U, 3 >& rhs) : x_(rhs.x_), y_(rhs.y_), z_(rhs.z_) {}

				const T& operator[](unsigned int i) const
				{
					return data[i];
				}

				T& operator[](unsigned int i)
				{
					return data[i];
				}

				const T& x()const {
					return x_;
				}
				const T& y()const {
					return y_;
				}
				const T& z()const {
					return z_;
				}
				//! Setter for x component
				void set_x(const T &x) { x_ = x; }

				//! Setter for y component
				void set_y(const T &y) { y_ = y; }

				//! Setter for z component
				void set_z(const T &z) { z_ = z; }

				bool operator ==(const VectorBase< T, 3 >& rhs) const
				{
					return ((x_ == rhs.x_) && (y_ == rhs.y_) && (z_ == rhs.z_));
				}

				bool operator !=(const VectorBase< T, 3 >& rhs) const
				{
					return !((*this) == rhs);
				}

				VectorBase< T, 3 >& operator +=(const VectorBase< T, 3 >& rhs)
				{
					x_ += rhs.x_;
					y_ += rhs.y_;
					z_ += rhs.z_;
					return *this;
				}

				VectorBase< T, 3 >& operator -=(const VectorBase< T, 3 >& rhs)
				{
					x_ -= rhs.x_;
					y_ -= rhs.y_;
					z_ -= rhs.z_;
					return *this;
				}

				VectorBase< T, 3 >& operator *=(const VectorBase< T, 3 >& rhs)
				{
					x_ *= rhs.x_;
					y_ *= rhs.y_;
					z_ *= rhs.z_;
					return *this;
				}

				VectorBase< T, 3 >& operator /=(const VectorBase< T, 3 >& rhs)
				{
					x_ /= rhs.x_;
					y_ /= rhs.y_;
					z_ /= rhs.z_;
					return *this;
				}

				VectorBase< T, 3 >& operator-()
				{
					x_ = -x_;
					y_ = -y_;
					z_ = -z_;
					return *this;
				}

				bool operator <(const VectorBase< T, 3 >& rhs) const
				{
					return (x_ < rhs.x_) && (y_ < rhs.y_) && (z_ < rhs.z_);
				}

				bool operator >(const VectorBase< T, 3 >& rhs) const
				{
					return (x_ > rhs.x_) && (y_ > rhs.y_) && (z_ > rhs.z_);
				}

				bool operator <=(const VectorBase< T, 3 >& rhs) const
				{
					return (x_ <= rhs.x_) && (y_ <= rhs.y_) && (z_ <= rhs.z_);
				}

				bool operator >=(const VectorBase< T, 3 >& rhs) const
				{
					return (x_ >= rhs.x_) && (y_ >= rhs.y_) && (y_.z >= rhs.z_);
				}

			};

			//partial specialization for 4 component vectors
			template< typename T >
			struct VectorBase< T, 4 >
			{
				typedef T ValueType;
				union
				{
					struct
					{
						T x_;
						T y_;
						T z_;
						T w_;
					};
					T data[4];
				};

				VectorBase< T, 4 >() {}
				VectorBase< T, 4 >(T n) : x_(n), y_(n), z_(n), w_(n) {}
				VectorBase< T, 4 >(T _x, T _y, T _z, T _w) : x_(_x), y_(_y), z_(_z), w_(_w) {}
				template< typename U >
				VectorBase< T, 4 >(const VectorBase< U, 4 >& rhs) : x_(rhs.x_), y_(rhs.y_), z_(rhs.z_), w_(rhs.w_) {}

				const T& operator[](unsigned int i) const
				{
					return data[i];
				}

				T& operator[](unsigned int i)
				{
					return data[i];
				}

				bool operator ==(const VectorBase< T, 4 >& rhs) const
				{
					return ((x_ == rhs.x_) && (y_ == rhs.y_) && (z_ == rhs.z_) && (w_ == rhs.w_));
				}

				bool operator !=(const VectorBase< T, 4 >& rhs) const
				{
					return !((*this) == rhs);
				}

				VectorBase< T, 4 >& operator +=(const VectorBase< T, 4 >& rhs)
				{
					x_ += rhs.x_;
					y_ += rhs.y_;
					z_ += rhs.z_;
					w_ += rhs.w_;
					return *this;
				}

				VectorBase< T, 4 >& operator -=(const VectorBase< T, 4 >& rhs)
				{
					x_ -= rhs.x_;
					y_ -= rhs.y_;
					z_ -= rhs.z_;
					w_ -= rhs.w_;
					return *this;
				}

				VectorBase< T, 4 >& operator *=(const VectorBase< T, 4 >& rhs)
				{
					x_ *= rhs.x_;
					y_ *= rhs.y_;
					z_ *= rhs.z_;
					w_ *= rhs.w_;
					return *this;
				}

				VectorBase< T, 4 >& operator /=(const VectorBase< T, 4 >& rhs)
				{
					x_ /= rhs.x_;
					y_ /= rhs.y_;
					z_ /= rhs.z_;
					w_ /= rhs.w_;
					return *this;
				}

				VectorBase< T, 4 >& operator-()
				{
					x_ = -x_;
					y_ = -y_;
					z_ = -z_;
					w_ = -w_;
					return *this;
				}

				bool operator <(const VectorBase< T, 4 >& rhs) const
				{
					return (x_ < rhs.x_) && (y_ < rhs.y_) && (z_ < rhs.z_) && (w_ < rhs.w_);
				}

				bool operator >(const VectorBase< T, 4 >& rhs) const
				{
					return (x_ > rhs.x_) && (y_ > rhs.y_) && (z_ > rhs.z_) && (w_ > rhs.w_);
				}

				bool operator <=(const VectorBase< T, 4 >& rhs) const
				{
					return (x_ <= rhs.x_) && (y_ <= rhs.y_) && (z_ <= rhs.z_) && (w_ <= rhs.w_);
				}

				bool operator >=(const VectorBase< T, 4 >& rhs) const
				{
					return (x_ >= rhs.x_) && (y_ >= rhs.y_) && (z_ >= rhs.z_) && (w_ >= rhs.w_);
				}
			};

			//specialization for 4 component unsigned char colors
			template<typename T>
			struct VectorColor //< unsigned char, 4>
			{
				typedef T ValueType;

				union
				{
					struct
					{
						unsigned char r;
						unsigned char g;
						unsigned char b;
						unsigned char a;
					};
					unsigned char data[4];
				};

				VectorColor< T >() : r(0), g(0), b(0), a(255) {}
				VectorColor< T >(ValueType n) : r(n), g(n), b(n), a(n) {}
				VectorColor< T >(ValueType _r, ValueType _g, ValueType _b, ValueType _a = 255) : r(_r), g(_g), b(_b), a(_a) {}
				VectorColor< T >(const VectorColor< ValueType >& rhs) : r(rhs.r), g(rhs.g), b(rhs.b), a(rhs.a) {}

				const ValueType& operator[](unsigned int i) const
				{
					return data[i];
				}

				ValueType& operator[](unsigned int i)
				{
					return data[i];
				}

				bool operator ==(const VectorColor< unsigned char >& rhs) const
				{
					return ((r == rhs.r) && (g == rhs.g) && (b == rhs.b) && (a == rhs.a));
				}

				bool operator !=(const VectorColor< unsigned char >& rhs) const
				{
					return !((*this) == rhs);
				}

				bool operator<(const VectorColor< unsigned char >& rhs) const
				{
					bool result = false;
					if (r < rhs.r)
					{
						result = true;
					}
					else if (r == rhs.r)
					{
						if (g < rhs.g)
						{
							result = true;
						}
						else if (g == rhs.g)
						{
							if (b < rhs.b)
							{
								result = true;
							}
							else if (b == rhs.b)
							{
								result = (a < rhs.a);
							}
						}
					}
					return result;
				}
			};

			//default typedefs
			typedef VectorBase< float, 2 > VecBase2f;   ///< 2D vector (float)
			typedef VectorBase< float, 3 > VecBase3f;   ///< 3D vector (float)
			typedef VectorBase< float, 4 > VecBase4f;   ///< 4D vector (float)
			typedef VectorBase< double, 2 > VecBase2d;  ///< 2D double (double)
			typedef VectorBase< double, 3 > VecBase3d;  ///< 3D double (double)
			typedef VectorBase< double, 4 > VecBase4d;  ///< 4D double (double)

			typedef VectorBase< int, 2 > VecBase2i;     ///< 2D vector (int)
			typedef VectorBase< int, 3 > VecBase3i;     ///< 3D vector (int)
			typedef VectorBase< int, 4 > VecBase4i;     ///< 4D vector (int)
			typedef VectorColor< unsigned char> Color;  ///< 32bit rgba color

			typedef VectorBase< int64_t, 2 > VecBase2ll;     ///< 3D vector (uint64)
			typedef VectorBase< int64_t, 3 > VecBase3ll;     ///< 3D vector (uint64)


			template< typename T, unsigned int N >
			std::ostream& operator<<(std::ostream& os, const VectorBase< T, N >& vec)
			{
				for (unsigned i = 0; i < N; ++i)
				{
					os << vec[i];
					if (i < N - 1)
						os << ',';
				}

				return os;
			}

			//overload for ValueType of unsigned char, should be streamed as a byte value, not a real character
			template< unsigned int N >
			std::ostream& operator<<(std::ostream& os, const VectorBase< unsigned char, N >& vec)
			{
				for (unsigned i = 0; i < N; ++i)
				{
					int val = vec[i];
					os << val;
					if (i < N - 1)
						os << ',';
				}

				return os;
			}

			template< typename T, unsigned int N >
			std::istream& operator>>(std::istream& is, VectorBase< T, N >& vec)
			{
				for (unsigned i = 0; i < N; ++i)
				{
					is >> vec[i];

					//read ',' seperators
					if (i < N - 1)
					{
						char ch;
						is >> ch;
					}
				}

				return is;
			}

			//overload for ValueType of unsigned char, should be streamed as a byte value, not a real character
			template< unsigned int N >
			std::istream& operator>>(std::istream& is, VectorBase< unsigned char, N >& vec)
			{
				for (unsigned i = 0; i < N; ++i)
				{
					int val;
					is >> val;
					vec[i] = int(val);

					//read ',' seperators
					if (i < N - 1)
					{
						char ch;
						is >> ch;
					}
				}

				return is;
			}

		} //namespace Math
	}
}
